module mculib.arm.bitband;


import mculib.arm.conf;
import std.traits;


enum bool SupportBitBand = imported!"mculib.baremetal".CheckTargetCPU!("cortex-m0","cortex-m0+","cortex-m3","cortex-m4","cortex-m7");


// 确认可用前提
static if (SupportBitBand)
{
    /// 可用bitband
    enum Bandables = [
        // 外设 bitband 地址范围
        BitBandable(0x4000_0000u,0x000F_FFFFu),
        // 内存 bitband 地址范围
        BitBandable(0x2000_0000u,0x000F_FFFFu),
    ];

    // 内存映射结构表
    struct BitBandable{
        /// 开始地址
        size_t RegionStart;
        /// 范围宽度
        size_t RegionSize;
        /// 结束地址
        //pragma(inline,true)
        @property size_t RegionEnd() @nogc pure
        {
            return ((RegionStart + RegionSize) - 1);
        }
    }

    /**
    * 检查地址是否可以使用`bitband`方式操作
    * Params:
    *  addr = 要操作的地址
    * Returns: true = 可以使用`bitband`方式操作
    */
    static bool CheckBitBandable(size_t addr) @nogc pure
    {
        return (
            ((addr >= Bandables[0].RegionStart) && (addr <= Bandables[0].RegionEnd)) ||
            ((addr >= Bandables[1].RegionStart) && (addr <= Bandables[1].RegionEnd))
            );
    }
    template CheckBitBandable(size_t addr)
    {
        enum CheckBitBandable = (
            ((addr >= Bandables[0].RegionStart) && (addr <= Bandables[0].RegionEnd)) ||
            ((addr >= Bandables[1].RegionStart) && (addr <= Bandables[1].RegionEnd))
            );
    }
    /**
    * 转换独立位操作方式为`bitband`方式
    * Params:
    *  addr = 要操作的地址
    *  bitnum = 要操作的位
    */
    size_t BitBand(in size_t addr,in size_t bitnum)  @nogc pure
    in(bitnum < 32)
    in(CheckBitBandable(addr))
    {
        return (addr & 0xF0000000) + 0x0200_0000 + ((addr & 0x000FFFFF) << 5) + (bitnum << 2);
    }
    template BitBand(size_t addr,size_t bitnum) 
    if((bitnum < 32) && CheckBitBandable!(addr))
    {       
        enum BitBand = (addr & 0xF0000000) + 0x0200_0000 + ((addr & 0x000FFFFF) << 5) + (bitnum << 2); 
    }


}else{
    alias CheckBitBandable = emptyFunc!bool;
    alias BitBand = emptyFunc!size_t;
}

